package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.repository;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.pagehelper.PageHelper;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ProcessFormStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ProcessInstanceStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.TaskLinkStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.FlowCatalogueService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.OuterDataSourceService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.*;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ResponseResult;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowFormExternalInstanceDO;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.model.FlowPlatformModelDetailDTO;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.monitor.FlowTaskTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.*;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.common.exception.BusiException;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.*;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.flowable.bpmn.model.Activity;
import org.flowable.engine.FormService;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.common.impl.cfg.IdGenerator;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.form.api.FormDeployment;
import org.flowable.form.api.FormRepositoryService;
import org.flowable.form.engine.impl.persistence.entity.FormResourceEntity;
import org.flowable.form.engine.impl.persistence.entity.FormResourceEntityImpl;
import org.flowable.task.api.Task;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author cheng.huaxing
 * @date 2019-02-27
 */
@Slf4j
@Service
public class FlowFormRepositoryImpl implements FlowFormRepository{

    @Autowired
    private FormRepositoryService formRepositoryService;

    @Autowired
    private FormResourcePersistRepository resourcePersistService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private FlowFormAppInfoDAO flowFormAppInfoDAO;

    @Autowired
    private ActFormResourceExtDAO actFormResourceExtDAO;

    @Autowired
    private IdGenerator idGenerator;

    @Autowired
    private FlowProcessInstanceTraceRepository processInstanceTraceRepository;

    @Autowired
    private FlowCatalogueService catalogueService;

    @Autowired
    private OuterDataSourceService dataSourceService;

    @Autowired
    private FlowTaskTraceRepository taskTraceService;

    @Autowired
    private FormService formService;

    @Autowired
    private FlowFormInstanceRepository formInstanceRepository;

    @Autowired
    private FormInstanceDraftRepository formInstanceDraftRepository;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private FlowFormExternalInfoDAO flowFormExternalInfoDAO;

    @Autowired
    private FlowFormExternalInstanceDAO flowFormExternalInstanceDAO;

    @Autowired
    TaskService taskService;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    FlowFormExternalHistoryDAO flowFormExternalHistoryDAO;

    @Autowired
    FlowFormFieldLimitDAO flowFormFieldLimitDAO;

    @Autowired
    FlowNextNodeCandidateRepository flowNextNodeCandidateRepository;

    @Autowired
    FlowTaskCandidateSettingsRepository flowTaskCandidateSettingsRepository;

    @Autowired
    HistoryService historyService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String create(FormCreateVO formCreateVO) {

        checkAuthorize(formCreateVO.getTenantId());
        //检验formKey是否存在
        checkFormExist(formCreateVO.getKey(), null);

        initForm(formCreateVO);

        // 保存表单信息
        FormResourceEntityImpl formResourceEntity = doAddForm(formCreateVO);
        // 保存表单与应用+业务的关联

        insertFlowFormAppInfo(formCreateVO, formResourceEntity.getId(), formCreateVO.getTenantId());

        return formResourceEntity.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean save(FormCreateVO formCreateVO) {

        FormResourceEntity resourceEntity = getFormResourceEntity(formCreateVO.getId());

        //校验key是否已存在
        checkFormExist(formCreateVO.getKey(), formCreateVO.getId());
        //根据表单状态执行操作：
        if (ProcessFormStatusEnum.UNPUBLISH.getCode().equals(formCreateVO.getStatus())) {
            // 当对未发布的表单进行修改时，会更新表单信息
            FormInfoVO formInfoVo = updateFormInfo(formCreateVO, resourceEntity);
            //更新表单-租户关联信息
            updateFormAppInfo(resourceEntity, formInfoVo);
        }
        if (ProcessFormStatusEnum.PUBLISH.getCode().equals(formCreateVO.getStatus())) {
            // 当对已发布的表单进行修改时，会生成一个更高的版本的表单
            formCreateVO.setVersion(formCreateVO.getVersion() + 1);
            this.create(formCreateVO);
        }
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String copy(FormCopyVO formCopyVO) {

        //1.校验访问权限
        checkAuthorize(formCopyVO.getTenantId());

        //2.校验formKey是否存在
        checkFormExist(formCopyVO.getKey(), null);

        FormCreateVO originFormEntity = getExistFormInfo(formCopyVO.getOriginFormId());

        /*
         * 一部分数据来自前端，一部分数据来自后端
         */
        FormCreateVO copyVo = resolveCopyFormVo(formCopyVO, originFormEntity);

        FormResourceEntityImpl formResourceEntity = doAddForm(copyVo);

        insertFlowFormAppInfo(formCopyVO, formResourceEntity.getId(), formCopyVO.getTenantId());

        return formResourceEntity.getId();
    }

    private FormCreateVO getExistFormInfo(String originFormId) {
        FormCreateVO originFormEntity = detail(originFormId);
        if (Objects.isNull(originFormEntity)) {
            throw new BusiException(ErrorCode.FORM_NOT_EXIST);
        }
        return originFormEntity;
    }

    private FormCreateVO resolveCopyFormVo(FormCopyVO formCopyVO, FormCreateVO originFormEntity) {
        FormCreateVO copyVo = new FormCreateVO();
        formCopyVO.setVersion(1);
        formCopyVO.setStatus(ProcessFormStatusEnum.UNPUBLISH.getCode());
        PropertyMapper mapper = PropertyMapper.get();
        if (Objects.isNull(formCopyVO.getTenantId())) {
            mapper.from(originFormEntity::getTenantId).whenNonNull().to(formCopyVO::setTenantId);
        }
        mapper.from(formCopyVO::getTenantId).to(copyVo::setTenantId);
        mapper.from(formCopyVO::getAppId).to(copyVo::setAppId);
        mapper.from(formCopyVO::getBusiId).to(copyVo::setBusiId);
        mapper.from(formCopyVO::getName).to(copyVo::setName);
        mapper.from(formCopyVO::getDescription).to(copyVo::setDescription);
        mapper.from(formCopyVO::getKey).to(copyVo::setKey);
        mapper.from(originFormEntity::getFields).to(copyVo::setFields);
        mapper.from(originFormEntity::getOutcomes).to(copyVo::setOutcomes);
        mapper.from(formCopyVO::getVersion).to(copyVo::setVersion);
        mapper.from(ProcessFormStatusEnum.UNPUBLISH::getCode).to(copyVo::setStatus);
        return copyVo;
    }


    @Override
    public void deploy(String formKey, String processInstanceId) {
        String formId = resolveDeployedForm(formKey);
        Assert.notNull(formId);
        FormResourceEntity resourceEntity = getFormResourceEntity(formId);
        //复制一份表单数据，关联模型 deploymentId
        FormResourceEntity deployFormEntity = new FormResourceEntityImpl();
        //改成实例级别
        deployFormEntity.setDeploymentId(processInstanceId);
        PropertyMapper mapper = PropertyMapper.get();
        mapper.from(resourceEntity::getName).to(deployFormEntity::setName);
        mapper.from(resourceEntity::getBytes).to(deployFormEntity::setBytes);
        resourcePersistService.insert(deployFormEntity);
    }

    @Override
    public FormCreateVO detail(String formId) {
        FormResourceEntity resourceEntity = resourcePersistService.find(formId);
        if (Objects.isNull(resourceEntity)) {
            return null;
        }
        FormCreateVO formCreateVO = convertToFormViewData(resourceEntity);
        if (Objects.isNull(formCreateVO)) {
            return null;
        }
        formCreateVO.setId(formId);
        //formCreateVO.setFields(FormUtils.getInstance().transformToViewForm(formCreateVO.getFields()));
        return formCreateVO;
    }

    @Override
    public List<FormFieldDTO> getFormFieldDetail(String formId) {
        List<FormFieldDTO> filedList = new ArrayList<>();
        FormCreateVO formCreateVO = detail(formId);
        if (Objects.nonNull(formCreateVO)){
            Object fields = formCreateVO.getFields();
            if (fields instanceof Map){
                Map fieldsMap = (Map) fields;
                Object fieldPropertiesObj = fieldsMap.get("properties");
                if (fieldPropertiesObj instanceof Map){
                    Map fieldProperties = (Map) fieldPropertiesObj;
                    Set<Map.Entry<String,Map>> entrySet = fieldProperties.entrySet();
                    Iterator<Map.Entry<String,Map>> iterator = entrySet.iterator();
                    while (iterator.hasNext()){
                        Map.Entry<String,Map> entry = iterator.next();
                        String fieldId = entry.getKey();
                        Map fieldParam = entry.getValue();
                        String componentType = String.valueOf(fieldParam.get("x-component"));
                        String fieldName = String.valueOf(fieldParam.get("title"));
                        FormFieldDTO formField = null;

                        if (Objects.equals("Attachment",componentType)){
                            formField = new FormFieldDTO(1);
                        }else{
                            formField = new FormFieldDTO(0);
                        }
                        formField.setFieldId(fieldId);
                        formField.setFieldName(fieldName);
                        filedList.add(formField);
                    }
                }
            }
        }
        return filedList;
    }

    @Override
    public ExternalFormDetailDTO externalDetail(String formKey) {
        FlowFormExternalInfoDO externalInfoDO =  flowFormExternalInfoDAO.selectByFormKey(formKey);
        //组装为DTO
        ExternalFormDetailDTO formDetailDTO =  new ExternalFormDetailDTO();
        formDetailDTO.setFormId(externalInfoDO.getFormId());
        BeanUtils.copyProperties(externalInfoDO,formDetailDTO);
        //如果该表单有属性
        if(Objects.nonNull(externalInfoDO.getFormPropertiesBytes())){
            Object properties = JsonUtils.toObject(new String(externalInfoDO.getFormPropertiesBytes(),StandardCharsets.UTF_8),Object.class);
            formDetailDTO.setProperties(properties);
        }
        return formDetailDTO;


    }

    private FormCreateVO convertToFormViewData(FormResourceEntity resourceEntity) {
        return JsonUtils.toObject(new String((resourceEntity.getBytes()), StandardCharsets.UTF_8), FormCreateVO.class);
    }


    private FormResourceEntity getFormResourceEntity(String formId) {
        FormResourceEntity resourceEntity = resourcePersistService.find(formId);
        if (Objects.isNull(resourceEntity)) {
            throw new BusiException(ErrorCode.FORM_NOT_EXIST);
        }
        return resourceEntity;
    }

    private FormInfoVO updateFormInfo(FormCreateVO formCreateVO, FormResourceEntity resourceEntity) {
        FormInfoVO formInfoVo = new FormInfoVO();
        BeanUtils.copyProperties(formCreateVO, formInfoVo);
        //formInfoVo.setFields(FormUtils.getInstance().transformToDBForm(formInfoVo.getFields()));
        resourceEntity.setName(formCreateVO.getKey() + ".form");
        resourceEntity.setBytes(JSON.toJSONString(formInfoVo).getBytes(StandardCharsets.UTF_8));
        resourcePersistService.update(resourceEntity);
        return formInfoVo;
    }

    /**
     * 确定是否为外置表单，对外提供接口
     * @param externalFormQueryDTO
     * @return
     */

    @Override
    public boolean isExternalForm(ExternalFormQueryDTO  externalFormQueryDTO){
        if(StringUtils.isNotEmpty(externalFormQueryDTO.getFormId())){
            return Objects.nonNull(flowFormExternalInfoDAO.selectByFormId(externalFormQueryDTO.getFormId()));
        }
        String formKey="";
        if(StringUtils.isNotEmpty(externalFormQueryDTO.getTaskId())){
            formKey = resolveFormKeyFromTaskId(externalFormQueryDTO.getTaskId());

        }
        if(StringUtils.isNotEmpty(externalFormQueryDTO.getInstanceId())){
            formKey = resolveFormKeyFromInstanceId(externalFormQueryDTO.getInstanceId());
        }

        if(StringUtils.isNotEmpty(externalFormQueryDTO.getProcessDefinitionKey())){
            formKey = resolveFormKeyFromDefinitionKey(externalFormQueryDTO.getProcessDefinitionKey());
        }

        FlowFormExternalInfoDO flowFormExternalInfoDO = flowFormExternalInfoDAO.selectByFormKey(formKey);
        return Objects.nonNull(flowFormExternalInfoDO);
    }

    /**
     * 根据流程实例id获取外部表单key值
     * @param instanceId
     * @return formkey
     * @author 司疆
     */

    @Override
    public String resolveFormKeyFromInstanceId(String instanceId){
        FlowProcessInstanceTraceVo processInstanceVo = processInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(instanceId);
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstanceVo.getProcessDefinitionId()).singleResult();
        if(processDefinition.hasStartFormKey()){
            return formService.getStartFormKey(processDefinition.getId());
        }
        return "";
    }
    /**
     * 查找流程模型绑定的外置表单描述信息文件
     * @param modelId
     */
    @Override
    public List<ExternalFormInfoTransferDTO> findBindExternalFormWithModel(String modelId) {
        Set<String> formKeys = getAllModelFormId(modelId);
        List<ExternalFormInfoTransferDTO> result = new ArrayList<>();
        if(formKeys.isEmpty()){
            return result;
        }
        List<FlowFormExternalInfoDO> flowFormExternalInfoDOList = flowFormExternalInfoDAO.selectByFormIdIn(formKeys);
        flowFormExternalInfoDOList.forEach(item->{
            result.add(covertFlowFormExternalInfoDOToTransferDTO(item));
        });

        return result;
    }

    /**
     * 批量插入外置表单描述信息，当前用于导入导出
     * @date 20210812
     * @param externalFormInfoTransferDTOList
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertExternalFormBatch(List<ExternalFormInfoTransferDTO> externalFormInfoTransferDTOList) {
        try{
            for (ExternalFormInfoTransferDTO transferDTO: externalFormInfoTransferDTOList) {
                FlowFormExternalInfoDO current =  convertTransferDTOToExternalFormInfo(transferDTO);
                //判断数据库中是否存在该formKey标识的表单,无则插入，有则更新
                FlowFormExternalInfoDO origin =  flowFormExternalInfoDAO.selectByFormKey(current.getFormKey());
                if(Objects.isNull(origin)){
                    flowFormExternalInfoDAO.insert(current);
                }else {
                    flowFormExternalInfoDAO.update(current);
                }
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }

    }

    @Override
    public boolean batchInsertFormFieldList(List<FormFieldLimitDomain> FormFieldLimitDomainList) {
        if (Objects.nonNull(FormFieldLimitDomainList) && !FormFieldLimitDomainList.isEmpty()){
            List<FlowFormFieldLimit> formFieldLimitList = new ArrayList<>(FormFieldLimitDomainList.size());
            FormFieldLimitDomainList.stream().forEach(formFieldLimitDomain -> {
                FlowFormFieldLimit flowFormFieldLimit = new FlowFormFieldLimit();
                BeanCopierUtils.copyProperties(formFieldLimitDomain,flowFormFieldLimit);
                formFieldLimitList.add(flowFormFieldLimit);
            });

            flowFormFieldLimitDAO.batchInsert(formFieldLimitList);
            return true;
        }
        return false;
    }

    @Override
    public List<FormFieldLimitDomain> getFormFieldLimitByNodeCode(String deployId, String formId, String nodeCode) {
        FlowFormFieldLimitExample flowFormFieldLimitExample = new FlowFormFieldLimitExample();
        flowFormFieldLimitExample.createCriteria().andDeployIdEqualTo(deployId)
                .andFormIdEqualTo(formId)
                .andNodeCodeEqualTo(nodeCode);
        List<FlowFormFieldLimit> flowFormFieldLimitList = flowFormFieldLimitDAO.selectByExample(flowFormFieldLimitExample);
        if (Objects.nonNull(flowFormFieldLimitList) && !flowFormFieldLimitList.isEmpty()){
            List<FormFieldLimitDomain> formFieldLimitDomainList = new ArrayList<>(flowFormFieldLimitList.size());
            flowFormFieldLimitList.stream().forEach(flowFormFieldLimit -> {
                FormFieldLimitDomain formFieldLimitDomain = new FormFieldLimitDomain();
                BeanCopierUtils.copyProperties(flowFormFieldLimit,formFieldLimitDomain);
                formFieldLimitDomainList.add(formFieldLimitDomain);
            });
            return formFieldLimitDomainList;
        }
        return null;
    }

    @Override
    public List<FormFieldLimitDomain> getFormFieldLimitByFlowCode(String deployId, String formId, String flowCode) {
        FlowFormFieldLimitExample flowFormFieldLimitExample = new FlowFormFieldLimitExample();
        flowFormFieldLimitExample.createCriteria().andDeployIdEqualTo(deployId)
                .andFormIdEqualTo(formId)
                .andFlowCodeEqualTo(flowCode);
        List<FlowFormFieldLimit> flowFormFieldLimitList = flowFormFieldLimitDAO.selectByExample(flowFormFieldLimitExample);
        if (Objects.nonNull(flowFormFieldLimitList) && !flowFormFieldLimitList.isEmpty()){
            List<FormFieldLimitDomain> formFieldLimitDomainList = new ArrayList<>(flowFormFieldLimitList.size());
            flowFormFieldLimitList.stream().forEach(flowFormFieldLimit -> {
                FormFieldLimitDomain formFieldLimitDomain = new FormFieldLimitDomain();
                BeanCopierUtils.copyProperties(flowFormFieldLimit,formFieldLimitDomain);
                formFieldLimitDomainList.add(formFieldLimitDomain);
            });
            return formFieldLimitDomainList;
        }
        return null;
    }

    /**
     * 提交流程时检查强制选人功能是否有遗漏点
     * @param taskId
     * @return
     */
    @Override
    public ResponseResult checkForcePickByForm(String taskId) {
        // 1、根据taskId查询任务
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 2、获取当前实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        // 3、获取活动
        Activity activity = (Activity) repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()).getFlowElement(task.getTaskDefinitionKey());
        // 定义强制选人Map和强制选人已选择处理人节点Map
        Map<String,String> forcePickMap = new HashMap<>();
        Map<String,String> setNodeMap = new HashMap<>();
        // 判断当前节点是“强制选人”节点，但是没有设置过候选人
        boolean isForcePick = false;
        boolean isNodeHadeler = false;
        Map<String,String> controlNodeMap = new HashMap<>();
        List<ModelNextNodeCandidateSettings> nodeSetList = null;
        List<ModelTaskCandidateSettingsDomain> taskCandidateList = flowTaskCandidateSettingsRepository.listModelTaskCandidateSettingsByDeployIdAndNodecode(processInstance.getDeploymentId(), activity.getId());
        if (taskCandidateList!=null && taskCandidateList.size()>0) {
            for (ModelTaskCandidateSettingsDomain vo : taskCandidateList) {
                nodeSetList = flowNextNodeCandidateRepository.selectByProcessInstanceIdAndNodeCode(processInstance.getId(), vo.getNodeCode());
                if ("1".equalsIgnoreCase(vo.getForcePick()) && !(nodeSetList!=null && nodeSetList.size()>0)) {
                    isForcePick = true;
                    break;
                }
                if ("1".equalsIgnoreCase(vo.getNextNodeHandler())) {
                    isNodeHadeler = true;
                }
            }
        }
        if (isForcePick) {
            return ResponseResult.fail(ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errCode(),ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errMessage());
        }
        // 初始化强制选人已设置的数据记录
        taskCandidateList = flowTaskCandidateSettingsRepository.listModelTaskCandidateSettingsByDeployIdAndNodecode(processInstance.getDeploymentId(), null);
        if (taskCandidateList!=null && taskCandidateList.size()>0) {
            for (ModelTaskCandidateSettingsDomain vo : taskCandidateList) {
                nodeSetList = flowNextNodeCandidateRepository.selectByProcessInstanceIdAndNodeCode(processInstance.getId(), vo.getNodeCode());
                if ("1".equalsIgnoreCase(vo.getForcePick()) && nodeSetList!=null && nodeSetList.size()>0) {
                    setNodeMap.put(vo.getNodeCode(),vo.getNodeCode());
                }
                if ("1".equalsIgnoreCase(vo.getNextNodeHandler()) && activity.getId().equalsIgnoreCase(vo.getNodeCode())) {
                    if (StringUtils.isNotEmpty(vo.getControlNode())) {
                        String[] strArray = vo.getControlNode().substring(1,vo.getControlNode().length()-1).split(",");
                        for (int i=0; i<strArray.length; i++) {
                            controlNodeMap.put(strArray[i].replaceAll("\"",""),strArray[i].replaceAll("\"",""));
                        }
                    }
                }
            }
            for (ModelTaskCandidateSettingsDomain vo : taskCandidateList) {
                if ("1".equalsIgnoreCase(vo.getForcePick()) && controlNodeMap.size()>0 && StringUtils.isNotEmpty(controlNodeMap.get(vo.getNodeCode()))) {
                    forcePickMap.put(vo.getNodeCode(),vo.getNodeCode());
                }
            }
        }
        // 判断当前节点配置了指定候选人，但是还没有全部指定处理人
        if (isNodeHadeler) {
            if (controlNodeMap.isEmpty()) {
                return ResponseResult.fail(ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errCode(),ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errMessage());
            }
            for (Map.Entry<String,String> entry : controlNodeMap.entrySet()) {
                if (StringUtils.isEmpty(setNodeMap.get(entry.getKey()))) {
                    isForcePick = true;
                    break;
                }
            }
            if (isForcePick) {
                return ResponseResult.fail(ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errCode(),ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errMessage());
            }
        }
        // 遍历所有强制选人节点是否都被设置了候选人配置
        for (Map.Entry<String,String> entry : forcePickMap.entrySet()) {
            if (StringUtils.isEmpty(controlNodeMap.get(entry.getKey()))) {
                isForcePick = true;
                break;
            }
        }
        if (isForcePick) {
            return ResponseResult.fail(ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errCode(),ErrorCode.EXIST_FORCE_PICK_NODE_NO_PROCESSED.errMessage());
        }

        return ResponseResult.success();
    }

    ExternalFormInfoTransferDTO covertFlowFormExternalInfoDOToTransferDTO(FlowFormExternalInfoDO infoDO){
        ExternalFormInfoTransferDTO externalFormInfoTransferDTO = new ExternalFormInfoTransferDTO();
        BeanUtils.copyProperties(infoDO,externalFormInfoTransferDTO);
        return externalFormInfoTransferDTO;
    }
    FlowFormExternalInfoDO convertTransferDTOToExternalFormInfo(ExternalFormInfoTransferDTO transferDTO){
        FormAnalyzeDTO formAnalyzeDTO = new FormAnalyzeDTO();
        formAnalyzeDTO.setFormDescriptionUrl(transferDTO.getFormDescriptionUrl());
        FlowFormExternalInfoDO flowFormExternalInfoDO = convertFlowFormExternalInfo(formAnalyzeDTO);
        flowFormExternalInfoDO.setFormKey(transferDTO.getFormKey());
        if (StringUtils.isBlank(flowFormExternalInfoDO.getFormId())){
            flowFormExternalInfoDO.setFormId(transferDTO.getFormId());
        }
//        FlowFormExternalInfoDO flowFormExternalInfoDO = new FlowFormExternalInfoDO();
//        BeanUtils.copyProperties(transferDTO,flowFormExternalInfoDO);
        return flowFormExternalInfoDO;
    }

    /**
     * 得到流程模式中所有的外置表单id
     *
     * @param modelId 模型id
     * @return {@link Set}<{@link String}>
     */
    private Set<String> getAllModelFormId(String modelId) {
        Set<String> result = new HashSet<>();
        FlowPlatformModelDetailDTO modelDetail = SpringUtils.getBean(FlowModelRepository.class).getModelDetail(modelId);
        if (modelDetail == null) {
            return result;
        }
        ProcessModelVo processModelVo = JsonUtils.toObject(JsonUtils.toJson(modelDetail.getModelJson()), ProcessModelVo.class);
        if (processModelVo == null) {
            return result;
        }
        // 检查外部properties启动表单
        JSONObject properties = JSON.parseObject(JsonUtils.toJson(processModelVo.getProperties()));
        if (properties.containsKey("formSource") && "externalForm".equals(properties.getString("formSource"))) {
            result.add(properties.getString("formId"));
        }
        // 检查各个节点properties表单
        List<ModelNodeVo> nodes = processModelVo.getNodes();
        for (ModelNodeVo node : nodes) {
            JSONObject modelProperties = JSON.parseObject(JsonUtils.toJson(node.getModel().getProperties()));
            if (modelProperties.containsKey("formSource") && "externalForm".equals(modelProperties.getString("formSource"))) {
                result.add(modelProperties.getString("formId"));
            }
        }
        return result;
    }

    /**
     * 通过流程定义获取表单key值
     * @param processDefinitionKey
     * @return formKey
     */
    @Override
    public String resolveFormKeyFromDefinitionKey(String processDefinitionKey){
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey)
                .active()
                .latestVersion()
                .singleResult();
        Assert.notNull(processDefinition);
        return formService.getStartFormKey(processDefinition.getId());
    }

    /**
     * 通过任务id获取表单key值
     * @param taskId
     * @return formKey
     */

    @Override
    public  String resolveFormKeyFromTaskId(String taskId){
        String formKey ="";
        FlowTaskTraceVo taskTrace = taskTraceService.getTaskTraceByTaskId(taskId);
        if (Objects.isNull(taskTrace)) {
            log.warn("{} 任务信息不存在", taskId);
            return formKey;
        }

        if (StringUtils.isBlank(taskTrace.getFormKey())) {
            log.warn("暂无表单信息");
            return formKey;
        }

        return taskTrace.getFormKey();
    }

    /**
     * 根据表单编码获取外置表单信息
     * @author 司疆
     */
    @Override
    public FlowFormExternalInfoDomain findExternalFormInfo(String formKey) {
        FlowFormExternalInfoDO  flowFormExternalInfoDO = flowFormExternalInfoDAO.selectByFormKey(formKey);
        if (Objects.nonNull(flowFormExternalInfoDO)){
            FlowFormExternalInfoDomain domain = new FlowFormExternalInfoDomain();
            BeanUtils.copyProperties(flowFormExternalInfoDO,domain);
            return domain;
        }
        return null;
    }

    @Override
    public FlowFormExternalInfoDomain findRenderExternalFormInfo(String formKey, String taskId) {
        FlowFormExternalInfoDO externalInfoDO = flowFormExternalInfoDAO.selectByFormKey(formKey);
        Map<String,Object> map = getFormVariables(null,taskId);
        String target = ParseRenderedVariable.parseRenderedVariable(externalInfoDO.getFormUrl(),map);
        externalInfoDO.setFormUrl(target);


        FlowFormExternalInfoDomain domain = new FlowFormExternalInfoDomain();
        BeanUtils.copyProperties(externalInfoDO,domain);
        return domain;
    }

    private void checkFormExist(String formKey, String formId) {
        FormResourceEntity existResourceEntity = resourcePersistService.find("0", formKey + ".form");

        //如果不存在，则直接返回
        if (Objects.isNull(existResourceEntity)) {
            return;
        }

        //如果formKey存在，且是同一条记录，则直接返回
        if (Objects.nonNull(formId)
                && existResourceEntity.getId().equals(formId)) {
            return;
        }

        throw new BusiException(ErrorCode.FORM_KEY_EXIST);
    }

    private void updateFormAppInfo(FormResourceEntity resourceEntity, FormInfoVO formInfoVo) {
        FlowFormAppInfo updateFormAppInfo = new FlowFormAppInfo();
        updateFormAppInfo.setFormName(formInfoVo.getName());
        updateFormAppInfo.setFormKey(formInfoVo.getKey());
        updateFormAppInfo.setUpdateTime(new Date());
        updateFormAppInfo.setDescription(formInfoVo.getDescription());
        updateFormAppInfo.setBusiId(formInfoVo.getBusiId());
        updateFormAppInfo.setAppId(formInfoVo.getAppId());
        FlowFormAppInfoExample formAppInfoExample = new FlowFormAppInfoExample();
        formAppInfoExample.createCriteria().andFormIdEqualTo(resourceEntity.getId());
        flowFormAppInfoDAO.updateByExampleSelective(updateFormAppInfo, formAppInfoExample);
    }

    @Override
    public Boolean delete(String formId) {
        //表单被关联不能删除
        if (isFormAssociated(formId)) {
            throw new BusiException(ErrorCode.FORM_ALREADY_ASSOCIATED);
        }
        //表单已发布状态不能删除
        FlowFormAppInfoExample formAppInfoExample = new FlowFormAppInfoExample();
        formAppInfoExample.createCriteria().andFormIdEqualTo(formId);
        List<FlowFormAppInfo> flowFormAppInfos = flowFormAppInfoDAO.selectByExample(formAppInfoExample);
        boolean isPublished = CollectionUtils.isEmpty(flowFormAppInfos) || ProcessFormStatusEnum.PUBLISH.getCode().equals(flowFormAppInfos.get(0).getStatus());
        if (isPublished) {
            throw new BusiException(ErrorCode.FORM_ALREADY_PUBLISHED);
        }
        //表单已部署不能删除
        FormResourceEntity resourceEntity = getFormResourceEntity(formId);
        boolean isDeployed = !(StringUtils.isEmpty(resourceEntity.getDeploymentId()) || StringUtils.isZero(resourceEntity.getDeploymentId()));
        if (isDeployed) {
            throw new BusiException(ErrorCode.FORM_ALREADY_DEPLOYED);
        }
        //执行表单逻辑删除
        FlowFormAppInfo param = new FlowFormAppInfo();
        param.setStatus(ProcessFormStatusEnum.DELETED.getCode());
        flowFormAppInfoDAO.updateByExampleSelective(param, formAppInfoExample);

        return true;
    }

    @Override
    public Boolean updateFormStatus(String formId, Integer status) {
        //表单存在性校验
        this.getFormResourceEntity(formId);

        if (ProcessFormStatusEnum.DELETED.getCode().equals(status)) {
            return this.delete(formId);
        }
        FlowFormAppInfoExample formAppInfoExample = new FlowFormAppInfoExample();
        formAppInfoExample.createCriteria().andFormIdEqualTo(formId);
        FlowFormAppInfo param = new FlowFormAppInfo();
        param.setStatus(status);
        flowFormAppInfoDAO.updateByExampleSelective(param, formAppInfoExample);
        return true;
    }

    @Override
    public Boolean isFormAssociated(String formId) {
        List<String> ids = actFormResourceExtDAO.isFormAssociated(formId);
        if (CollectionUtils.isNotEmpty(ids)) {
            return true;
        }
        return false;
    }

    @Override
    public Boolean deploymentDelete(String formDeployId) {
        FormDeployment formDeployment = formRepositoryService.createDeploymentQuery().deploymentId(formDeployId).singleResult();
        if (Objects.isNull(formDeployment)) {
            return null;
        }
        formRepositoryService.deleteDeployment(formDeployId);
        return true;
    }

    @Override
    public FormCreateVO findDeploymentById(String formDeployId) {
        FormDeployment formDeployment = formRepositoryService.createDeploymentQuery().deploymentId(formDeployId).singleResult();
        if (Objects.isNull(formDeployment)) {
            return null;
        }
        FormResourceEntity resourceEntity = resourcePersistService.find(formDeployment.getId(), formDeployment.getName());
        if (Objects.isNull(resourceEntity)) {
            return null;
        }
        FormCreateVO formCreateVO = convertToFormViewData(resourceEntity);
        if (Objects.nonNull(formCreateVO)) {
            //formCreateVO.setFields(FormUtils.getInstance().transformToViewForm(formCreateVO.getFields()));
        }
        return formCreateVO;
    }

    @Override
    public FormCreateVO findFormDeployment(String formKey, String processDefinitionId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        if (Objects.isNull(processDefinition)) {
            return null;
        }

        return doFindFormResourceEntity(processDefinition.getDeploymentId(), formKey);
    }

    @Override
    public FormCreateVO findDeployForm(String formKey) {
        //1.根据表单key，查询已发布的表单
        String formId = resolveDeployedForm(formKey);
        if (StrUtil.isBlank(formId)) {
            return null;
        }
        return detail(formId);
    }

    @Override
    public String findFormIdByFormKey(String formKey) {
        String formId = resolveDeployedForm(formKey);
        return formId;
    }

    private String resolveDeployedForm(String formKey) {
        FlowFormAppInfoExample query = new FlowFormAppInfoExample();
        query.createCriteria().andFormKeyEqualTo(formKey).andStatusEqualTo(ProcessFormStatusEnum.PUBLISH.getCode());
        List<FlowFormAppInfo> forms = flowFormAppInfoDAO.selectByExample(query);
        if (CollectionUtils.isNotEmpty(forms) && Objects.nonNull(forms.get(0).getFormId())) {
            return forms.get(0).getFormId();
        }
        return null;
    }

    private FormCreateVO doFindFormResourceEntity(String processInstanceId, String formKey) {
        FormResourceEntity formEntity = resourcePersistService.find(processInstanceId, formKey + ".form");
        if (Objects.nonNull(formEntity)) {
            return convertToFormViewData(formEntity);
        }
        return null;
    }

    @Override
    public ProcessInstanceStartFormDetailVo findStartFormDetail(String processInstanceId) {
        ProcessInstanceStartFormDetailVo result = new ProcessInstanceStartFormDetailVo();
        FlowProcessInstanceTraceVo processInstanceVo = processInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstanceId);
        if (Objects.isNull(processInstanceVo)) {
            log.warn("流程实例不存在");
            return result;
        }

        //
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstanceVo.getProcessDefinitionId()).singleResult();
        if (processDefinition.hasStartFormKey()) {

            FormCreateVO metadata = findFormMetadataAndData(processInstanceId, null, formService.getStartFormKey(processDefinition.getId()), true);
            BeanUtils.copyProperties(metadata, result);
        }

        result.setProcessInstanceId(processInstanceId);

        //todo 针对外部数据源，覆盖options
        //result.setFields(dataSourceService.processDataSourceConfigAfterSubmit(result.getFields(), processInstanceId, null));
        return result;
    }

    /**
     * 根据流程实例id或者taskid获取流程变量
     * @return
     */
    @Override
    public Map<String,Object> getFormVariables(String processInstanceId, String taskId){
        Map<String,Object> variables = new HashMap<>();
        if(StringUtils.isNotBlank(taskId)){
            FlowTaskTraceVo taskTrace = taskTraceService.getTaskTraceByTaskId(taskId);
            if (Objects.nonNull(taskTrace) && Objects.equals(taskTrace.getLinkStatus(), TaskLinkStatusEnum.DONE.getCode())){
                List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery().taskId(taskId).list();
                if (CollectionUtils.isNotEmpty(historicVariableInstances)){
                    for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) {
                        variables.put(historicVariableInstance.getVariableName(),historicVariableInstance.getValue());
                    }
                }
                // 当根据taskId取不到变量或者只能取到approval变量时，重新拿取流程变量，避免出现表单变量取不到的情绪
                if(CollectionUtils.isEmpty(historicVariableInstances)||(historicVariableInstances.size()==1&&variables.containsKey("approval"))){
                    historicVariableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(taskTrace.getProcessInstanceId()).list();
                    if (CollectionUtils.isNotEmpty(historicVariableInstances)){
                        for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) {
                            variables.put(historicVariableInstance.getVariableName(),historicVariableInstance.getValue());
                        }
                    }
                }
                log.info("getFormVariables historicVariableInstances variables={}",JSON.toJSON(variables));
            }else{
                variables = taskService.getVariables(taskId);
                variables.putAll(taskService.getVariablesLocal(taskId));
                log.info("getFormVariables task variables={}",JSON.toJSON(variables));
            }
        }
        if(StringUtils.isNotBlank(processInstanceId)){
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            if (Objects.nonNull(processInstance)){
                variables = runtimeService.getVariables(processInstanceId);
            }else{
                List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
                if (CollectionUtils.isNotEmpty(historicVariableInstances)){
                    for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) {
                        variables.put(historicVariableInstance.getVariableName(),historicVariableInstance.getValue());
                    }
                }
            }
            log.info("getFormVariables processInstance variables={}",JSON.toJSON(variables));
        }
        return variables;

    }

    /**
     * 获取用户填报的数据表单,
     * @param processInstanceId
     * @param taskId
     * @return
     */
    @Override
    public ObjectNode filledExternalFormInfo(String processInstanceId,String taskId) {
        log.info("filledExternalFormInfo res:processInstanceId="+processInstanceId+",taskId="+taskId);
        //设置查询条件
        FlowFormExternalInstanceDO flowFormExternalInstanceDO = new FlowFormExternalInstanceDO();
        // 整个流程表单的key
        String processFormKey = null;
        // 单个任务节点表单的key
        String taksNodeFormKey = null;
        //如果查询实例表单
        if(StringUtils.isNotEmpty(processInstanceId)){
            flowFormExternalInstanceDO.setProcessInstanceId(processInstanceId);
            processFormKey = resolveFormKeyFromInstanceId(processInstanceId);
        }
        //如果查询的是任务表单
        if(StringUtils.isNotEmpty(taskId)){
            String processInsId = taskTraceService.getTaskTraceByTaskId(taskId).getProcessInstanceId();
            taksNodeFormKey = resolveFormKeyFromTaskId(taskId);
            flowFormExternalInstanceDO.setProcessInstanceId(processInsId);
        }
        // 表单key
        String formKey = null;
        // 如果整个流程表单不为空并且节点表单不为空,则优先选择节点表单
        if(StringUtils.isNotBlank(processFormKey) &&
                StringUtils.isNotBlank(taksNodeFormKey)) {
            flowFormExternalInstanceDO.setFormKey(taksNodeFormKey);
            formKey = taksNodeFormKey;
        } else if(StringUtils.isBlank(processFormKey) &&
                StringUtils.isNotBlank(taksNodeFormKey)) {
            flowFormExternalInstanceDO.setFormKey(taksNodeFormKey);
            formKey = taksNodeFormKey;
        } else if(StringUtils.isNotBlank(processFormKey) &&
                StringUtils.isBlank(taksNodeFormKey)) {
            flowFormExternalInstanceDO.setFormKey(processFormKey);
            formKey = processFormKey;
        }

        // 查询配置的节点外置表单或全局的表单
        FlowFormExternalInfoDomain infoDomain = findExternalFormInfo(formKey);
        //拿到绑定url
        String originFormUrl = infoDomain.getFormUrl();
        //进行变量渲染
        Map<String,Object> variables = getFormVariables(processInstanceId,taskId);
        log.info("originFormUrl:"+originFormUrl);
        String targetFormUrl = ParseRenderedVariable.parseRenderedVariable(originFormUrl,variables);
        log.info("targetFormUrl:"+targetFormUrl);
        //targetFormUrl = targetFormUrl + (targetFormUrl.contains("?")?"&":"?")+"formId="+formId;
        ObjectNode externalFormNode = JsonUtils.getMapper().createObjectNode();
        externalFormNode.put("formName",infoDomain.getFormName());
        externalFormNode.put("formUrl", targetFormUrl);
        if(StringUtils.isNotEmpty(taskId)){
            Map<String,String> display =  analyzeNodeDisplay(taskId);
            for (String key:display.keySet()) {
                externalFormNode.put(key,display.get(key));
            }
        }
        return externalFormNode;

    }
    /**
     * 通过taskid获取对应节点的展示方式
     * @param taskId
     */
    private Map<String,String> analyzeNodeDisplay(String taskId) {
        Map<String,String> display =  new HashMap<>();
        //获取任务所属流程对应模型文件
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if(Objects.isNull(task)) {
            return new HashMap<>();
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName() + ".json");
        ProcessModelVo processModel = ModelUtils.getInstance().streamToModel(inputStream);
        //获取任务对应节点编码
        String taskKey = task.getTaskDefinitionKey();
        List<Object> childShapes = processModel.getChildShapes();
        //获取任务对应流程节点数据
        for (Object childShape : childShapes) {
            JsonNode jsonNode = JsonUtils.readTree(JsonUtils.toJson(childShape));
            if (jsonNode != null) {
                //首先是用户任务节点
                String type = jsonNode.get("stencil").get("id").asText();
                if ("UserTask".equals(type)) {
                   //判断当前节点配置的节点编码
                    String currentKey = jsonNode.get("properties").get("overrideid").asText();
                    JsonNode displayStyle = jsonNode.get("properties").get("displayStyle");
                    if(taskKey.equals(currentKey)&&Objects.nonNull(displayStyle)){
                        display.put("displayStyle",displayStyle.asText());
                        JsonNode showAsUrl = jsonNode.get("properties").get("showAsUrl");
                        if(Objects.nonNull(showAsUrl)){
                            display.put("showAsUrl",showAsUrl.asText());
                        }
                        return display;
                    }

                }
            }
        }

        //如果未指定，那么使用标准方式
        if(display.isEmpty()){
            display.put("displayStyle","standard");
            display.put("showAsUrl","N");
        }
        return display;
    }

    /**
     * 历史 外置表单获取
     * @param taskId
     * @return
     */
    @Override
    public HistoryExternalTaskFormDTO historyExternalTaskForm(String taskId) {
        //taskid获取历史
        HistoryExternalTaskFormDTO historyExternalTaskFormDTO = new HistoryExternalTaskFormDTO();
        historyExternalTaskFormDTO.setTaskId(taskId);

        FlowFormExternalHistoryDO historyDO = flowFormExternalHistoryDAO.selectExternalFormRecord(taskId);
        //如果该节点任务没有绑定历史任务表单，同时其所在实例，没有绑定外置全局表单，那么直接返回空
        if(Objects.isNull(historyDO)){
            return historyExternalTaskFormDTO;
        }
        //如果该节点绑定了外置表单，也就是historyDO中的taskFormInstanceUrl不为空
        if(StringUtils.isNotEmpty(historyDO.getTaskFormInstanceUrl())){
            String taskFormUrl = historyDO.getTaskFormInstanceUrl();

            //将taskId返回给业务系统
            if (org.apache.commons.lang3.StringUtils.isNotBlank(taskId)){
                taskFormUrl = taskFormUrl + (taskFormUrl.contains("?") ? "&" : "?") + "taskId=" + taskId;
            }

            //设置任务表单url
            historyExternalTaskFormDTO.setTaskFormUrl(taskFormUrl);
            //解析任务对应formKey
            String formKey =  resolveFormKeyFromTaskId(taskId);
            //设置任务表单名称
            historyExternalTaskFormDTO.setTaskFormName(flowFormExternalInfoDAO.selectByFormKey(formKey).getFormName());
        }
        return historyExternalTaskFormDTO;
    }

    @Override
    public HistoryExternalProcessFormDTO historyExternalProcessForm(String processInstanceId) {
        HistoryExternalProcessFormDTO historyExternalProcessFormDTO = new HistoryExternalProcessFormDTO();
        historyExternalProcessFormDTO.setProcessInstanceId(processInstanceId);

        FlowFormExternalHistoryDO historyDO = flowFormExternalHistoryDAO.selectExternalFormRecord(processInstanceId);
        //如果该节点任务没有绑定历史任务表单，同时其所在实例，没有绑定外置全局表单，那么直接返回空
        if(Objects.isNull(historyDO)){
            return historyExternalProcessFormDTO;
        }
        //如果该节点对应流程实例绑定了外置表单，也就是historyDO中的processFormInstanceUrl不为空
        if(StringUtils.isNotEmpty(historyDO.getProcessFormInstanceUrl())){
            //设置流程表单url
            historyExternalProcessFormDTO.setProcessFormUrl(historyDO.getProcessFormInstanceUrl());
            //解析流程对应formkey
            String formKey = resolveFormKeyFromInstanceId(processInstanceId);
            historyExternalProcessFormDTO.setProcessFormName(flowFormExternalInfoDAO.selectByFormKey(formKey).getFormName());
        }
        return historyExternalProcessFormDTO;
    }

    @Override
    public TaskFormDetailVo findTaskFormDetail(String taskId, boolean includeData) {
        if (StringUtils.isBlank(taskId)) {
            return new TaskFormDetailVo();
        }

        FlowTaskTraceVo taskTrace = taskTraceService.getTaskTraceByTaskId(taskId);

        if (Objects.isNull(taskTrace)) {
            log.warn("{} 任务信息不存在", taskId);
            return new TaskFormDetailVo();
        }

        return doGetTaskFormDetail(taskTrace, includeData);
    }


    private TaskFormDetailVo doGetTaskFormDetail(FlowTaskTraceVo taskTrace, boolean includeData) {
        TaskFormDetailVo result = new TaskFormDetailVo();
        if (StringUtils.isBlank(taskTrace.getFormKey())) {
            log.warn("暂无表单信息");
            return result;
        }
        FormCreateVO formMetadataAndData = findFormMetadataAndData(taskTrace.getProcessInstanceId(), taskTrace.getTaskId(), taskTrace.getFormKey(), includeData);
        BeanCopierUtils.copyProperties(formMetadataAndData, result);
        result.setProcessInstanceId(taskTrace.getProcessInstanceId());
        result.setTaskId(taskTrace.getTaskId());
        return result;
    }

    @Override
    public List<TaskFormDetailVo> findTaskFormDetail(List<FlowTaskTraceVo> tasks, boolean includeData) {
        List<TaskFormDetailVo> result = new ArrayList<>(tasks.size());
        for (FlowTaskTraceVo task : tasks) {
            result.add(doGetTaskFormDetail(task, includeData));
        }
        return result;
    }

    private FormCreateVO findFormMetadataAndData(String processInstanceId, String taskId, String formKey, boolean includeData) {
        FormCreateVO result = new FormCreateVO();
        FlowProcessInstanceTraceVo processInstanceVo = processInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstanceId);
        if (Objects.isNull(processInstanceVo)) {
            log.warn("流程实例不存在");
            return result;
        }
        //ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstanceVo.getProcessDefinitionId()).singleResult();
        FormCreateVO formMetadata = doFindFormResourceEntity(processInstanceId, formKey);
        if (Objects.nonNull(formMetadata)) {
            BeanUtils.copyProperties(formMetadata, result);
        }

        if (!includeData) {
            //如果不需要查询填报的数据，则直接返回元数据即可
            return result;
        }

        FormInstanceVo formInstance = resolveFormInstance(processInstanceId, taskId);
        if (Objects.nonNull(formInstance)) {
            result.setFieldData(formInstance.getValues());
        }

        return result;
    }

    private FormInstanceVo resolveFormInstance(String processInstanceId, String taskId) {
        //根据任务状态，若任务还在进行中，则查询草稿，若任务已经结束，则查询表单实例数据表
        //当然，这里也可以只要是查任务相关的，都查草稿，但草稿的数据量可能会比较大，还是分开来查
        if (Objects.isNull(taskId)) {
            //查询流程实例级别的表单数据
            return formInstanceRepository.getFormInstance(processInstanceId, taskId);
        }
        FlowTaskTraceVo taskTrace = taskTraceService.getTaskTraceByTaskId(taskId);
        //任务已经办理了，则查表单实例数据表
        if (isCompleted(taskTrace)) {
            return formInstanceRepository.getFormInstance(processInstanceId, taskId);
        }
        //否则，查草稿表
        FormInstanceDraft latestFormInstanceDraft = formInstanceDraftRepository.getLatestFormInstanceDraft(taskId);

        //如果是驳回的，则加载出上一次填写的内容，直接用processInstanceId + taskNodeCode查询
        if (Objects.isNull(latestFormInstanceDraft)) {
            latestFormInstanceDraft = formInstanceDraftRepository.getLatestFormInstanceDraft(processInstanceId, taskTrace.getActivityId());
        }

        return convert(latestFormInstanceDraft);
    }

    private FormInstanceVo convert(FormInstanceDraft latestFormInstanceDraft) {
        FormInstanceVo result = new FormInstanceVo();
        if (Objects.nonNull(latestFormInstanceDraft)
                && Objects.nonNull(latestFormInstanceDraft.getResource())) {
            result.setValues(JSONObject.parseObject(new String(latestFormInstanceDraft.getResource())));
        }
        return result;
    }

    private boolean isCompleted(FlowTaskTraceVo taskTrace) {
        return Objects.nonNull(taskTrace.getCompleteTime()) || Objects.equals(TaskLinkStatusEnum.DONE.getCode(), taskTrace.getLinkStatus());
    }

    private List<Object> resolveOuterSourceConfig(List<Object> fields) {
        return dataSourceService.processDataSourceConfig(fields);
    }

    @Override
    public List<FlowFormAppInfoVo> list(FormQueryVO formQueryVO) {
        if (catalogueService.checkTenantAuthorize(formQueryVO)) {
            return Collections.emptyList();
        }
        PageHelper.startPage(formQueryVO.getPageNum(), formQueryVO.getPageSize());
        return flowFormAppInfoDAO.selectList(formQueryVO);
    }

    /**
     * 司疆增加，通过url获取外置表单信息
     * @param formAnalyzeDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public FlowExternalFormInfoDTO analyzeExternalFormInfo(FormAnalyzeDTO formAnalyzeDTO) {
        //通过url获取外置表单描述信息
        String restUrl = formAnalyzeDTO.getFormDescriptionUrl();
        restUrl = org.apache.commons.lang3.StringUtils.trim(restUrl);
        ExternalFormDescriptionDTO  descriptionDTO = restTemplate.getForObject(restUrl,ExternalFormDescriptionDTO.class);

        //如果第三方提供数据提供formid，那么使用第三方
        if(StringUtils.isBlank(descriptionDTO.getFormId())){
           descriptionDTO.setFormId(idGenerator.getNextId());
        }
        FlowFormExternalInfoDO current = convertFlowFormExternalInfo(descriptionDTO, formAnalyzeDTO);
        //插入表单
        flowFormExternalInfoDAO.insert(current);
        return convertToFlowExternalFormInfoDTO(current,descriptionDTO);
    }

    private FlowFormExternalInfoDO convertFlowFormExternalInfo(FormAnalyzeDTO formAnalyzeDTO){
        //通过url获取外置表单描述信息
        String restUrl = formAnalyzeDTO.getFormDescriptionUrl();
        restUrl = org.apache.commons.lang3.StringUtils.trim(restUrl);
        ExternalFormDescriptionDTO descriptionDTO = restTemplate.getForObject(restUrl, ExternalFormDescriptionDTO.class);
        return convertFlowFormExternalInfo(descriptionDTO, formAnalyzeDTO);
    }


    private FlowExternalFormInfoDTO convertToFlowExternalFormInfoDTO(FlowFormExternalInfoDO flowFormExternalInfoDO,ExternalFormDescriptionDTO descriptionDTO){
        FlowExternalFormInfoDTO flowExternalFormInfoDTO = new FlowExternalFormInfoDTO();
        flowExternalFormInfoDTO.setFormId(flowFormExternalInfoDO.getFormId());
        flowExternalFormInfoDTO.setFormName(flowFormExternalInfoDO.getFormName());
        flowExternalFormInfoDTO.setFormKey(flowFormExternalInfoDO.getFormKey());
        Object properties = descriptionDTO.getProperties();
        if (Objects.nonNull(properties)){
            List<FormFieldDTO> formFieldDTOList = new ArrayList<>();
            if (properties instanceof List){
                List<Map> propertiesList = (List) properties;
                propertiesList.stream().forEach(propertie -> {
                    FormFieldDTO formFieldDTO = null ;
                    Object type = propertie.get("type");
                    int fieldType = 0;
                    if (Objects.nonNull(type)){
                        fieldType = Integer.valueOf(String.valueOf(type));
                    }
                    formFieldDTO = new FormFieldDTO(fieldType);
                    formFieldDTO.setFieldId(String.valueOf(propertie.get("code")));
                    formFieldDTO.setFieldName(String.valueOf(propertie.get("name")));

                    formFieldDTOList.add(formFieldDTO);

                });
            }
            flowExternalFormInfoDTO.setFormFields(formFieldDTOList);
        }
        return flowExternalFormInfoDTO;
    }

    private FlowFormAppInfoVo convert(FlowFormAppInfo entity) {
        if (Objects.isNull(entity)) {
            return null;
        }
        FlowFormAppInfoVo result = new FlowFormAppInfoVo();
        BeanUtils.copyProperties(entity, result);
        return result;
    }

    private FormCreateVO findForm(String formDeploymentId, String resourceName) {
        FormResourceEntity resourceEntity = resourcePersistService.find(formDeploymentId, resourceName);
        if (Objects.isNull(resourceEntity)) {
            return null;
        }
        FormCreateVO formCreateVO = convertToFormViewData(resourceEntity);
        if (Objects.nonNull(formCreateVO)) {
            //formCreateVO.setFields(FormUtils.getInstance().transformToViewForm(formCreateVO.getFields()));
            //todo 处理外部数据源配置
            //formCreateVO.setFields(resolveOuterSourceConfig(formCreateVO.getFields()));
        }
        return formCreateVO;
    }

    private FormResourceEntityImpl doAddForm(FormCreateVO formCreateVO) {
        FormResourceEntityImpl formResourceEntity = new FormResourceEntityImpl();
        if(StringUtils.isNotEmpty(formCreateVO.getId())){
            formResourceEntity.setId(formCreateVO.getId());
        }
        formResourceEntity.setName(formCreateVO.getKey() + ".form");
        formResourceEntity.setBytes(JSON.toJSONString(formCreateVO).getBytes(StandardCharsets.UTF_8));
        // 默认设置部署id
        formResourceEntity.setDeploymentId("0");
        resourcePersistService.insert(formResourceEntity);
        return formResourceEntity;
    }

    private void insertFlowFormAppInfo(FormInfoVO formInfoVO, String formId, Integer tenantId) {
        User user = userRepository.getCurrentUser();
        FlowFormAppInfo flowFormAppInfo = new FlowFormAppInfo();
        flowFormAppInfo.setId(idGenerator.getNextId());
        if (Objects.nonNull(tenantId)) {
            flowFormAppInfo.setTenantId(tenantId);
        }
        flowFormAppInfo.setFormId(formId);
        flowFormAppInfo.setDescription(formInfoVO.getDescription());
        flowFormAppInfo.setCreatorId(user.getId());
        flowFormAppInfo.setCreatorName(user.getUserName());
        flowFormAppInfo.setFormName(formInfoVO.getName());
        flowFormAppInfo.setFormKey(formInfoVO.getKey());
        flowFormAppInfo.setCreateTime(new Date());
        flowFormAppInfo.setUpdateTime(new Date());
        flowFormAppInfo.setAppId(formInfoVO.getAppId());
        flowFormAppInfo.setBusiId(formInfoVO.getBusiId());
        flowFormAppInfo.setStatus(formInfoVO.getStatus());
        flowFormAppInfo.setVersion(formInfoVO.getVersion());
        flowFormAppInfoDAO.insertSelective(flowFormAppInfo);
    }

    /**
     * 根据前端描述信息以及应用方提供数据转化为DO
     * @param descriptionDTO
     * @param formAnalyzeDTO
     * @return
     */
    private FlowFormExternalInfoDO convertFlowFormExternalInfo(ExternalFormDescriptionDTO descriptionDTO, FormAnalyzeDTO formAnalyzeDTO){

        FlowFormExternalInfoDO flowFormExternalInfoDO = new FlowFormExternalInfoDO();
        //formKey一律由流程引擎生成保证唯一性
        flowFormExternalInfoDO.setFormKey(idGenerator.getNextId());
        flowFormExternalInfoDO.setFormId(descriptionDTO.getFormId());
        flowFormExternalInfoDO.setFormName(descriptionDTO.getFormName());
        flowFormExternalInfoDO.setDescription(descriptionDTO.getDescription());
        flowFormExternalInfoDO.setGetDataUrl(descriptionDTO.getGetDataUrl());
        flowFormExternalInfoDO.setFormUrl(descriptionDTO.getFormUrl());
        flowFormExternalInfoDO.setFormSource(descriptionDTO.getFormSource());
        flowFormExternalInfoDO.setFormDescriptionUrl(formAnalyzeDTO.getFormDescriptionUrl());
        //二进制属性转化
        if(Objects.nonNull(descriptionDTO.getProperties())){
            flowFormExternalInfoDO.setFormPropertiesBytes(JSON.toJSONString(descriptionDTO.getProperties()).getBytes(StandardCharsets.UTF_8));
        }
        return flowFormExternalInfoDO;

    }

    private void checkAuthorize(Integer tenantId) {
        if (Objects.nonNull(tenantId) && !catalogueService.hasAuthorize(tenantId)) {
            throw new BusiException(ErrorCode.NO_AUTHORITY);
        }
    }

    private void initForm(FormCreateVO formCreateVo) {
        Integer version = null != formCreateVo.getVersion() ? formCreateVo.getVersion() : 1;
        formCreateVo.setVersion(version);
        formCreateVo.setStatus(ProcessFormStatusEnum.UNPUBLISH.getCode());
        formCreateVo.setFields(new JSONObject());
        formCreateVo.setOutcomes(new ArrayList<>());
    }
}
